home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
pascal
/
fulcom.zip
/
FULCOM.IBM
next >
Wrap
Text File
|
1986-09-16
|
20KB
|
464 lines
{ include file FULCOM.IBM
IBM COMMUNICATIONS PROCEDURES}
{ Gary Hartman 73537,1362}
{
These procedures are part of a set of two comm port include files that
are identical to the programmer, but handle the different hardware that
is found in the TI Professional computer and the IBM PC computer. This
enables you to write a applications programs that can be compiled for
either machine with a simple include file substitution.
The programmer interface includes full buffering for both Input and OUTPUT.
Output buffering allows an applications program to output a complex screen
or control data quickly, allowing the host computer to remain free for
use even if the selected baud rate is relative slow with respect to the
amount of data output. These procedures were originally developed for
use in a control system program that operated a remote terminal and remote
hardware interface driven from one computer.
Much of the IBM interrupt procedures have been developed from the file
INTERR.INC found in CompuServe Turbo Pascal SIG. The TI Professional procedures
are based on the articles by Matt Lawrence in June 1986 issue of the
magazine TI Professional Computing (Publications & Communications Inc.;
12416 Hymeadow Drive, Suite Two; Austin TX 78750-1896). The basic data
structure was based on Matt Lawrence's ideas.
The file FULCOM.PAS is a basic dumb-terminal program that demonstrates
the use of these procedures.
*****************************************************************************
READ THIS READ THIS READ THIS READ THIS READ THIS READ THIS READ THIS
The variables that are at a global level in the include file must remain
there. Due to the nature of the 8088 interrupts and Turbo Pascal these
procedures must not be included inside any another procedures or functions.
You must declare outside (or include it inside this file) the following:
type WorkLineType=string[255];
var RegisterSet case integer of
1: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:integer);
2: (AL,AH,BL,BH,CL,CH,DL,DH:byte);
end;
DSStorage:integer absolute CSeg:$00A0;
The variable RegisterSet is used for DOS calls (handy for almost any program);
the type WorkLineType is use for string output; the variable DSStorage is used
to store the contents of the DS register for use by the interrupt procedures
to restore the DS register. You must at the begining of the program make
the assignment:
DSStorage:=dseg;
With out this the any interrupt handler will not be able to access any
global variables.
******************************************************************************
USER ENTRY POINTS
The buffers are known as InputBuffer[Com] and OutputBuffer[Com], where
Com:byte is the comm port number (1 or 2). The overflow flag is addressed
as InputBuffer[Com].Over or OutputBuffer[Com].Over.
procedure SENDSTRING(Com:byte;S:WorkLineType);
This inserts a string of characters into the selected OutputBuffer
and initiates transmission in the transmitter is quiet.
function BUFFEREMPTY(B:BufferType):boolean;
Returns true if the buffer is empty. The logical value of:
not(BUFFEREMPTY(InputBuffer[Com]))
can be compared to the keypressed function of Turbo Pascal.
function READBUFFER(B:BufferType):byte;
Returns the rear of the selected buffer (usually an input buffer).
You must check the buffer with not(BUFFEREMPTY(InputBuffer[Com]))
before invoking this function. As written, only the lower 7
bits are kept on incoming data. If you need all 8 simply remove
the "anding" of the input data with $7F in the interrupt procedures
(COMINTERRUPT1 or COMINTERRUPT2).
procedure INITCOM(
ComNum:byte;Baud:integer;Parity:char;WordSize:byte;StopBits:byte);
Initiates the selected comm port to the values passed,sets the
interrupt vector to point to the user installed routine, and
asserts the DTR line.
procedure TERMINATECOM(Com:byte);
Un-asserts the DTR line and re-installs the original interrupt
vectors for the selected comm port.
function CDSTATUS:boolean;
True if the DCD (Data Carrier Detect-pin 8) is asserted; otherwise
false.
function CTSSTATUS:boolean;
True if the CTS (Clear To Send-pin 5) is asserted; otherwise
false.
procedure TXCONTROL(Com:byte;Assert:boolean);
Sets the DTR line accroding to Assert.
******************************************************************************
}
const BufferSize=4095;
type { Type declarations }
Busy = Array [1..2] of boolean;
BufferType=record
Head,Tail:integer; { front, rear pointers }
Over:boolean; { queue error, full ect }
Data:array[0..BufferSize] of byte;
end;
ComBaseType=array[1..2] of integer;
CONST
irq4 = $30; { Interrupt vector address for }
{ COM1. }
irq3 = $2C; { Vector for COM2. }
ComBase:ComBaseType=($03F8,$02F8); { port addresses }
{ Offset to add to com#base for}
intenreg = 1; { Interrupt enable register }
intidreg = 2; { Interrupt id register }
linectrl = 3; { Line control register }
modemctrl = 4; { Modem control register }
linestat = 5; { Line status register }
modemstat = 6; { Modem status register }
eoi = $20; { End of interrupt command }
var
ComVecSeg, { Segment of DOS set }
ComVecOff:array[1..2] of integer; { Offset of DOS set com int. }
Tbyte,Lbyte:integer; { global var for interrupt }
ComBusy:array[1..2] of boolean; { Comport trans busy flags }
InputBuffer:array[1..2] of BufferType; { input buffer }
OutputBuffer:array[1..2] of BufferType; { output buffer }
procedure COMINTERRUPT1;
begin
inline($50/$53/$51/$52/$57/$56/$06/$1E/$2E/$A1 /$A0 /$00/$50/$1F);
Lbyte:=port[ComBase[1]+intidreg]; { Get Interrupt ID }
Lbyte:=(lbyte shr 1) and $03; { Isolate ID bits }
If Lbyte=1 then begin { Check for Transmit done }
if OutputBuffer[1].Tail=OutputBuffer[1].Head then ComBusy[1]:=false
else begin { output another character }
OutputBuffer[1].Tail:=(OutputBuffer[1].Tail+1)
mod sizeof(OutputBuffer[1].Data);
port[ComBase[1]]:=OutputBuffer[1].Data[OutputBuffer[1].Tail];
end;
end
else if Lbyte=2 then begin { Check for Received Data }
Tbyte:=port[ComBase[1]]; { Get the character in the port}
Lbyte:=port[ComBase[1]+linestat]; { Get the status of the port }
if (InputBuffer[1].Head+1) mod sizeof(InputBuffer[1].Data)=
InputBuffer[1].Tail
then InputBuffer[1].Over:=true { buffer overflow }
else begin { put into buffer }
InputBuffer[1].Head:=(InputBuffer[1].Head+1)
mod sizeof(InputBuffer[1].Data);
InputBuffer[1].Data[InputBuffer[1].Head]:=Tbyte and $7F;
{ use only first 7 bits }
end;
end; { lbyte = 2 }
port[$20]:=$20; { signal end of interrupt code }
inline($1F/$07/$5E/$5F/$5A/$59/$5B/$58/$5D/$89 /$EC/$5D/$CF );
end; {COMINTERRUTP1}
procedure COMINTERRUPT2;
begin
inline($50/$53/$51/$52/$57/$56/$06/$1E/$2E/$A1 /$A0 /$00/$50/$1F);
Lbyte:=port[ComBase[2]+intidreg]; { Get Interrupt ID }
Lbyte:=(lbyte shr 1) and $03; { Isolate ID bits }
If Lbyte=1 then begin { Check for Transmit done }
if OutputBuffer[2].Tail=OutputBuffer[2].Head then ComBusy[2]:=false
else begin { output another character }
OutputBuffer[2].Tail:=(OutputBuffer[2].Tail+1)
mod sizeof(OutputBuffer[2].Data);
port[ComBase[2]]:=OutputBuffer[2].Data[OutputBuffer[2].Tail];
end;
end
else if Lbyte=2 then begin { Check for Received Data }
Tbyte:=port[ComBase[2]]; { Get the character in the port}
Lbyte:=port[ComBase[2]+linestat]; { Get the status of the port }
if (InputBuffer[2].Head+1) mod sizeof(InputBuffer[2].Data)=
InputBuffer[2].Tail
then InputBuffer[2].Over:=true { buffer overflow }
else begin { put into buffer }
InputBuffer[2].Head:=(InputBuffer[2].Head+1)
mod sizeof(InputBuffer[2].Data);
InputBuffer[2].Data[InputBuffer[2].Head]:=Tbyte and $7F;
{ use only first 7 bits }
end;
end; { lbyte = 2 }
port[$20]:=$20; { signal end of interrupt code }
inline($1F/$07/$5E/$5F/$5A/$59/$5B/$58/$5D/$89 /$EC/$5D/$CF );
end; {COMINTERRUTP2}
procedure TXCONTROL(Com:byte;Assert:boolean);
{Sets the DTR line to Assert}
var TByte:byte;
begin {DTRSET}
inline($FA); { disable interrupts }
TByte:=Port[ComBase[Com]+ModemCtrl]; { get current state }
if Assert then TByte:=TByte or $01 { assert DTR }
else TByte:=TByte and $FE; { un-assert DTR }
Port[ComBase[Com]+ModemCtrl]:=TByte; { rewrite ModemCtrl Reg }
inline($FB); { turn interrupts back on}
end; {TXCONTROL}
procedure INTON(Com:byte);
const
DtrTrue=1;
RtsTrue=2;
Bit3True=8;
var
Tbyte : byte; { Temperary byte buffer }
begin {INTON}
Tbyte:=port[ComBase[Com]]; { Read the ports to clear any }
Tbyte:=port[ComBase[Com]+linestat ]; { error conditions }
port[ComBase[Com]+modemctrl]:=DtrTrue+RtsTrue+Bit3True;
port[ComBase[Com]+intenreg]:=3; { Enable com port interrupts }
TByte:=port[$21];
with RegisterSet do begin
AX:=$2500; { Load the function number for }
{ redefining an interrupt }
DS:=cseg; { Get and set the segment and }
case com of
1:dx:=ofs(COMINTERRUPT1); { offset of the handler }
2:dx:=ofs(COMINTERRUPT2); { offset of the handler }
end;
end;
case com of
1:begin
ComVecOff[1]:=memw[0000:irq4]; { Save the segment and offset }
ComVecSeg[1]:=memw[0000:irq4+2]; {of the DOS interrupt handler }
RegisterSet.AX:=RegisterSet.AX+$0C; { Use the COM1: interrupt }
intr($21,RegisterSet); { Call DOS to reset INT 0C }
port[$21]:=TByte and $EF;
end;
2:begin
ComVecOff[2]:=memw[0000:irq3]; { Same as above }
ComVecSeg[2]:=memw[0000:irq3+2];
RegisterSet.AX:=RegisterSet.AX+$0B;{ Use the COM2: interrupt }
intr($21,RegisterSet); { Call DOS }
port[$21]:=TByte and $F7;
end;
end;
ComBusy[com]:=false; { Com port not busy }
inline($FB); { Enable interrupts }
end; {INTON}
procedure CLEAR(var Buf:BufferType);
{ this procedure clears a Buffer }
begin {CLEAR}
Buf.Tail:=1;
Buf.Head:=1;
Buf.Over:=false;
end; {CLEAR}
procedure INITCOM(Com:byte;R:integer;P:char;B:byte;S:byte);
const
Bits7=2;
Bits8=3;
Stopbit1=0; { These are constants used }
Stopbit2=4; { to define parity, stop bits, }
Noparity=0; { data bits, etc. }
Oddparity=8;
Evenparity=24;
Rate300=0;
Rate1200=64;
Rate2400=160;
Rate4800=192;
Rate9600=224;
var
Tlcr, { Line control register }
TDLmsb, { Divisor latch MSB }
TDLlsb : byte; { Divisor latch LSB }
Bits : integer; { No of bits per char }
StopBits : integer; { No of stop bits per char }
SetParity : integer; { parity mode even, odd , none }
begin {INITCOM}
TDLmsb:=0; { Set DL MSB to 0 for 1200, }
{ 2400, 4800 and 9600 baud }
case R of { Use case to check baud rate }
300:begin { Check for 300 baud }
TDLmsb:=1; { Set DL MSB to 01 }
TDLlsb:=$80; { Set DL LSB to 80 }
end; { for a total of 0180 }
1200:TDLlsb:=$60; { 1200 set LSB to 60 }
2400:TDLlsb:=$30; { 2400 set LSB to $30 }
4800:TDLlsb:=$18; { 4800 set LSB to 18 }
9600:TDLlsb:=$0c; { 0C for 9600 baud }
end;
case P of { use case to check parity }
'E' : setparity:=evenparity; { set for even parity }
'O' : setparity:=oddparity; { set for odd parity }
'N' : setparity:=noparity; { set for no parity }
else SetParity:=NoParity; { default is no parity }
end;
case S of { use case for stopbits }
1:StopBits:=Stopbit1; { one stopbit }
2:stopbits:=Stopbit2; { two stopbits }
else Stopbits:=Stopbit1; { default to 1 stopbit }
end;
case B of { use case for bits per char }
8:Bits:=bits8; { set to eight bits }
7:Bits:=bits7; { set to seven bits }
else bits:=bits8; { default to eight bits }
end;
inline($FA); { disable interupts }
Tlcr:=port[ComBase[Com]]; { read the port to clear any }
Tlcr:=port[ComBase[Com]+LineStat]; { error condition }
port[ComBase[Com]+LineCtrl]:=Bits+StopBits+SetParity; { Set parameters }
Tlcr:=port[ComBase[Com]+Linectrl]; { Get the Line control register}
port[ComBase[Com]+linectrl]:=tlcr or $80; { Set Div Latch Access Bit }
port[ComBase[Com]]:=TDLlsb; { in order to access divisor }
port[ComBase[Com]+1]:=TDLmsb; { latches, then store values }
port[ComBase[Com]+LineCtrl]:=Tlcr and $7F;{ clear the DLAB }
inline($FB); { interupts on }
CLEAR(InputBuffer[Com]); { clear the buffers }
CLEAR(OutputBuffer[Com]);
INTON(Com); { establish our vectors }
TXCONTROL(Com,true); { assert DTR }
end; {INITCOM}
procedure TERMINATECOM(Com:byte);
var
TByte:byte;
begin
TXCONTROL(Com,false);
inline($FA); { CLI } { Disable interrupts }
TByte:=port[$21]; { }
port[ComBase[Com]+IntenReg]:=0; { Disable COM interrupts }
if Com=1 then port[$21]:=TByte or $10 { turn off interrupt control}
else port[$21]:=TByte or $08;
memw[0000:irq4]:=ComVecOff[Com]; { Restore the DOS interrupt }
memw[0000:irq4+2]:=ComVecSeg[Com]; { handler }
ComBusy[Com]:=true;
inline($FB);
end;
function BUFFERFULL(var Buf:BufferType):boolean;
{ this fuction tests to see it the queue is full }
var Temp:integer;
begin
Temp:=(Buf.Head+1) mod sizeof(Buf.Data);
BufferFull:=(Temp=Buf.Tail);
end;
function BUFFEREMPTY(var Buf:BufferType):boolean;
{ this function test to see if the queue is empty }
begin
BUFFEREMPTY:=Buf.Tail=Buf.Head;
end;
procedure WRITEBUFFER(var Buf:BufferType;var Input:char);
{ this procedure adds an entry to the queue at the rear }
var Temp:integer;
begin
if BUFFERFULL(Buf) then Buf.Over:=true {overflow condition }
else begin { there is room}
Temp:=(Buf.Head+1) mod sizeof(Buf.Data);
Buf.Data[Temp]:=ord(Input);
Buf.Head:=Temp;
end;
end; {BUFFERWRITE}
function READBUFFER(var Buf:BufferType):byte;
{ this procedure removes an entry from the queue at the front }
begin
Buf.Tail:=(Buf.Tail+1) mod sizeof(Buf.Data);
ReadBuffer:=Buf.Data[Buf.Tail];
end;
procedure SENDSTRING(Com:byte;S:WorkLineType);
{ this is the main output routine, which sends a string }
var I,J:byte;
begin
J:=ord(S[0]); { get length of string }
for I:=1 to J do WRITEBUFFER(OutputBuffer[Com],S[I]); {output to buf}
if not(ComBusy[Com]) and not(BUFFEREMPTY(OutputBuffer[Com]))
then begin { tickle the transmitter to start output}
ComBusy[Com]:=true;
port[ComBase[Com]]:=READBUFFER(OutputBuffer[Com]);
end;
end; {SENDSTRING}
function DCDSTATUS(Com:byte):boolean;
{ Returns the stats of the carier detect line }
begin {DCDSTATUS}
with RegisterSet do begin
AH:=$03;
DX:=pred(Com);
intr($14,RegisterSet);
DCDStatus:=(AL and $80)=$80;
end;
end; {DCDSTATUS}
function CTSSTATUS(Com:byte):boolean;
{ returns the status of the clear to send line}
begin {CTSSTATUS}
with RegisterSet do begin
AH:=$03;
DX:=pred(Com);
intr($14,RegisterSet);
CTSStatus:=(AL and $10)=$10;
end;
end; {CTSSTATUS}